﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.ServiceProcess;
using System.Text;


namespace WUTOS.DataBase.DataBaseDAL
{
    public class DAL
    {
 
        /// <summary>
        /// 数据库连接字符窜设置
        /// </summary>
        /// <param name="loginMdel">登陆模式：1（windows模式），2（混合模式）</param>
        /// <param name="databaseModel">数据库实体</param>
        /// <returns>连接字符窜</returns>
        public static string SetConnectionString(int loginMdel, DataBaseInfo databaseModel)
        {
            string ConnectionString = "";
            if (loginMdel == 1)
            {
                ConnectionString = "Data Source=" + databaseModel.DataBaseSourse
                                    + " ; " + "Integrated Security=True " + "; " + "Database = " + databaseModel.InitialCatalog
                                    + "; " + "Connection Timeout=" + databaseModel.ConnectTimeout + " ;Connection lifetime=0;min pool size = 1;max pool size=50";
            }
            else
            {
                ConnectionString = "Data Source= " + databaseModel.DataBaseSourse + ";" + "Initial Catalog=" + databaseModel.InitialCatalog
                                 + ";" + "User Id=" + databaseModel.UserID + ";" + "Password=" + databaseModel.PassWord
                                 + "; " + "Connection Timeout=" + databaseModel.ConnectTimeout + " ;Connection lifetime=0;min pool size = 1;max pool size=50";
            }
            return ConnectionString;
        }
 
        /// <summary>
        /// 数据库连接测试
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true:连接成功，false：连接失败</returns>
        public static bool ConnectionTest(string ConnectionString, ref string logstring)
        {
            
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                try
                {
                    sqlcon.Open();
                    logstring = "";
                    return true;
                }
                catch (System.Exception ex)
                {
                    logstring += ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }

            }
        }

        /// <summary>
        /// 清除连接池
        /// </summary>
        public static void ClearAllPool()
        {
            SqlConnection.ClearAllPools();
        }

        /// <summary>
        /// 创建数据库，连接字符窜设为master
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databaseName">数据库名</param>
        /// <param name="path">数据库文件存放路径</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：成功，false：失败</returns>
        public static bool CreateEmptyDataBase(string ConnectionString, string databaseName, string path, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "";
                if (path == "")
                {
                    sqlstr = "CREATE DATABASE \"" + databaseName + "\"";
                }
                else
                {
                    sqlstr = "CREATE DATABASE \"" + databaseName + "\" ON " +
                            "PRIMARY (NAME= \"" + databaseName +
                             "_DATA\", FILENAME=\'" + path + "\\" + databaseName + ".mdf\'" +
                             ") LOG ON (NAME=\"" + databaseName + "_log\"," +
                             "FILENAME=\'" + path + "\\" + databaseName + ".ldf\')";
                }
                try
                {
                    SqlConnection.ClearAllPools();
                    SqlCommand sqlCom = new SqlCommand(sqlstr, sqlcon);
                    sqlcon.Open();
                    sqlCom.ExecuteNonQuery();
                    logstring += "";
                    return true;

                }
                catch (System.Exception ex)
                {
                    logstring += "\r\n" + ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 检测数据库是否存在，连接字符窜数据库设为master
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databaseName">数据库名</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：存在，false:当无错误提示时，数据库不存在，否则连接错误</returns>
        public static bool IsDataBaseExist(string ConnectionString, string databaseName, ref string logstring)
        {
            string sqlstring = "select count(name) from sys.databases where name = N'" + databaseName + "'";

            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                try
                {
                    DataSet ds = new DataSet();
                    SqlDataAdapter sqldata = new SqlDataAdapter(sqlstring, sqlcon);
                    sqlcon.Open();
                    sqldata.Fill(ds);
                    logstring += "";
                    return (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0 &&
                           int.Parse(ds.Tables[0].Rows[0][0].ToString()) >= 1) ? true : false;

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }

        }

        /// <summary>
        /// 删除数据库，连接字符串数据库设为master
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">待删除数据库名称</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：删除成功，false：删除失败</returns>
        public static bool DeleteDataBase(string ConnectionString, string databasename, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "drop database " + databasename;
                try
                {
                    SqlCommand sqlCom = new SqlCommand(sqlstr, sqlcon);
                    sqlcon.Open();
                    sqlCom.ExecuteNonQuery();
                    logstring = "";
                    return true;
                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 利用脚本创建数据库,已经建立空库
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">数据库名</param>
        /// <param name="path">数据库文件存放路径</param>
        /// <param name="scriptname">脚本名称</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：创建成功，false：创建失败</returns>
        public static bool CreateDataBaseByScript(string ConnectionString, string databasename, string path, string scriptname, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                List<string> scriptline = new List<string>();
                scriptline = ScriptToCommandLine(scriptname);
                try
                {
                    sqlcon.Open();
                    SqlCommand sqlCom = new SqlCommand();
                    SqlTransaction sqlTran = sqlcon.BeginTransaction();
                    sqlCom.Connection = sqlcon;
                    sqlCom.Transaction = sqlTran;
                    #region -事务-

                    try
                    {
                        foreach (string sqlcomtext in scriptline)
                        {
                            sqlCom.CommandText = sqlcomtext;
                            sqlCom.ExecuteNonQuery();
                        }
                        sqlTran.Commit();
                        logstring += "";
                        return true;

                    }
                    catch (System.Exception ex)
                    {
                        sqlTran.Rollback();
                        logstring = ex.Message + "\r\n";
                        return false;
                    }
                    #endregion

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message; ;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }

            }
        }

        /// <summary>
        /// 备份数据库
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">待备份数据库名</param>
        /// <param name="filename">备份文件</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：备份成功，false：备份失败</returns>
        public static bool BackUpDataBase(string ConnectionString, string databasename, string filename, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "BACKUP DATABASE" + "\"" + databasename + "\"" + "TO DISK='" + filename + "'";
                try
                {
                    SqlCommand sqlCom = new SqlCommand(sqlstr, sqlcon);
                    sqlcon.Open();
                    sqlCom.ExecuteNonQuery();
                    logstring += "";
                    return true;
                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 还原数据库
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">数据库名</param>
        /// <param name="filename">备份文件</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：成功，false：失败</returns>
        public static bool RestoreDataBaseByBackUp(string ConnectionString, string databasename, string filename, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "";
                string mdfFileName = "";
                string mdf_logfileName = "";
                string diskDataBaseName = "";
                string diskDataBase_logName = "";
                SqlCommand sqlCom = new SqlCommand();
                sqlCom.Connection = sqlcon;

                GetDataBaseMdfLdfName(ConnectionString, databasename, ref mdfFileName, ref mdf_logfileName, ref logstring);
                GetBackUpDataBaseName(ConnectionString, filename, ref diskDataBaseName, ref diskDataBase_logName, ref logstring);

                try
                {

                    #region -备份-
                    sqlcon.Open();
                    sqlstr = "restore database \"" + databasename + "\" from disk=\'" + filename + "\'" + " with MOVE \'"
                              + diskDataBaseName + "\' TO \'" + mdfFileName + "\'" + ",MOVE \'" + diskDataBase_logName + "\'TO \'"
                              + mdf_logfileName + "\' , NOUNLOAD ,REPLACE,STATS = 10";

                    sqlCom.CommandText = sqlstr;
                    sqlCom.ExecuteNonQuery();


                    sqlstr = "";
                    if (diskDataBaseName != databasename)
                    {
                        sqlstr = "ALTER DATABASE " + databasename + " MODIFY FILE  (NAME = '"
                                + diskDataBaseName + "', NEWNAME = '" + databasename + "')";
                    }
                    if (diskDataBase_logName != (databasename + "_log"))
                    {
                        sqlstr += Environment.NewLine;
                        sqlstr = "ALTER DATABASE " + databasename + " MODIFY FILE  (NAME = '" + diskDataBase_logName + "', NEWNAME = '" + databasename + "_log')";
                    }
                    if (sqlstr != string.Empty)
                    {
                        sqlCom.CommandText = sqlstr;
                        sqlCom.ExecuteNonQuery();
                    }
                    #endregion

                    logstring += "";
                    return true;
                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 获取备份数据库文件中的数据库和日志的逻辑名
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="fileNameofBackUpDataBase">备份数据库文件名</param>
        /// <param name="databaseName">数据库名</param>
        /// <param name="logName">日志文件名</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true:成功，false：失败</returns>
        public static bool GetBackUpDataBaseName(string ConnectionString, string fileNameofBackUpDataBase, ref string databaseName, ref string logName, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "restore FILELISTONLY FROM DISK = '" + fileNameofBackUpDataBase + "'";
                try
                {
                    SqlCommand sqlCom = new SqlCommand();
                    SqlDataReader sqlreader;
                    sqlCom.Connection = sqlcon;
                    sqlCom.CommandText = sqlstr;
                    sqlcon.Open();
                    sqlreader = sqlCom.ExecuteReader();

                    sqlreader.Read();
                    databaseName = sqlreader.GetString(0);
                    sqlreader.Read();
                    logName = sqlreader.GetString(0);
                    sqlreader.Close();
                    logstring += "";
                    return true;

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 获取数据库的mdf及ldf文件名
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">目标数据库</param>
        /// <param name="mdfname">mdf文件名</param>
        /// <param name="ldfname">ldf文件名</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：成功，false：失败</returns>
        public static bool GetDataBaseMdfLdfName(string ConnectionString, string databasename, ref string mdfname, ref string ldfname, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "select *  from sysdatabases where name = '" + databasename + "'";
                try
                {
                    sqlcon.Open();
                    SqlCommand sqlCom = new SqlCommand();
                    SqlDataReader sqlreader;
                    sqlCom.Connection = sqlcon;
                    sqlCom.CommandText = sqlstr;
                    sqlreader = sqlCom.ExecuteReader();

                    sqlreader.Read();
                    mdfname = sqlreader.GetString(10);
                    ldfname = mdfname.Remove(mdfname.LastIndexOf(".")) + "_log.ldf";
                    sqlreader.Close();
                    logstring += "";
                    return true;

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 关闭数据库所有使用进程
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="databasename">数据库名</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：成功，</returns>
        public static bool KillDataBaseProcess(string ConnectionString, String databasename, ref string logstring)
        {
            StringBuilder sqlProcCheck = new StringBuilder();
            StringBuilder sqlProc = new StringBuilder();
            sqlProcCheck.Append("if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]') and OBJECTPROPERTY(id, N'IsProcedure') = 1) ");
            sqlProcCheck.Append("drop   procedure   [dbo].[p_killspid] ");

            sqlProc.Append("create   proc   p_killspid ");
            sqlProc.Append("@dbname   varchar(200)  ");
            sqlProc.Append("as     ");
            sqlProc.Append("declare   @sql     nvarchar(500)     ");
            sqlProc.Append("declare   @spid   nvarchar(20) ");
            sqlProc.Append("declare   #tb   cursor   for ");
            sqlProc.Append("select   spid=cast(spid   as   varchar(20))   from   master..sysprocesses   where   dbid=db_id(@dbname) ");
            sqlProc.Append("open   #tb ");
            sqlProc.Append("fetch   next   from   #tb   into   @spid ");
            sqlProc.Append("while   @@fetch_status=0 ");
            sqlProc.Append("begin     ");
            sqlProc.Append("exec( 'kill   '+@spid) ");
            sqlProc.Append("fetch   next   from   #tb   into   @spid ");
            sqlProc.Append("end     ");
            sqlProc.Append("close   #tb ");
            sqlProc.Append("deallocate   #tb ");

            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                sqlcon.Open();
                SqlCommand sqlCom = new SqlCommand();
                //SqlTransaction sqlTran = sqlcon.BeginTransaction();
                sqlCom.CommandType = CommandType.Text;
                sqlCom.Connection = sqlcon;
                //sqlCom.Transaction = sqlTran;
                try
                {
                    try
                    {
                        sqlCom.CommandText = sqlProcCheck.ToString();
                        sqlCom.ExecuteNonQuery();
                        sqlCom.CommandText = sqlProc.ToString();
                        sqlCom.ExecuteNonQuery();
                    }
                    catch (System.Exception ex)
                    {
                        logstring = ex.Message;
                        return false;
                    }

                    sqlCom.CommandText = "p_killspid";
                    sqlCom.CommandType = CommandType.StoredProcedure;
                    sqlCom.Parameters.AddWithValue("@dbname", databasename);
                    sqlCom.ExecuteNonQuery();
                    //sqlTran.Commit();
                    logstring += "";
                    return true;
                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    //sqlTran.Rollback();
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }


            }
        }

        /// <summary>
        /// 获取服务器版本信息
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>服务器版本信息</returns>
        public static string GetSqlServerVersionInfo(string ConnectionString, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "select @@version";
                string version = "";
                try
                {
                    SqlDataAdapter sqldata = new SqlDataAdapter(sqlstr, sqlcon);
                    DataSet ds = new DataSet();
                    sqlcon.Open();
                    sqldata.Fill(ds);
                    if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0 && ds.Tables[0].Rows[0][0] != System.DBNull.Value)
                    {
                        version = ds.Tables[0].Rows[0][0].ToString();
                        version = version.Contains("Express") ? "SQL Server Express" : "SQL Server Developer";
                    }
                    else
                    {
                        logstring += "获取版本信息失败!\r\n";
                    }

                    return version;

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return version;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// 获取服务器数据库信息
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>服务器数据库信息链表</returns>
        public static List<DataBaseInfo.Database> GetSqlServerDataBaseInfo(string ConnectionString, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "select * from sysdatabases where version is null";
                DataSet ds = new DataSet();
                List<DataBaseInfo.Database> databaseInfoList = new List<DataBaseInfo.Database>();
                try
                {
                    SqlDataAdapter sqldata = new SqlDataAdapter(sqlstr, sqlcon);
                    sqlcon.Open();
                    sqldata.Fill(ds);
                    foreach (DataRow dr in ds.Tables[0].Rows)
                    {
                        DataBaseInfo.Database databaseInfo;
                        databaseInfo.databasename = dr[0].ToString();
                        databaseInfo.databaseCreateTime = dr[4].ToString();
                        databaseInfoList.Add(databaseInfo);
                    }
                    return databaseInfoList;
                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return databaseInfoList;
                }
                finally
                {
                    sqlcon.Close();
                }

            }
        }

        /// <summary>
        /// 获取服务器用户信息
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>服务器用户信息链表</returns>
        public static List<string> GetSqlServerUserName(string ConnectionString, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                string sqlstr = "select name from syslogins";
                DataSet ds = new DataSet();
                List<string> usernameList = new List<string>();
                try
                {
                    SqlDataAdapter sqldata = new SqlDataAdapter(sqlstr, sqlcon);
                    sqlcon.Open();
                    sqldata.Fill(ds);
                    if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0 && ds.Tables[0].Rows[0][0] != System.DBNull.Value)
                    {
                        foreach (DataRow dr in ds.Tables[0].Rows)
                        {
                            string username = dr[0].ToString();
                            usernameList.Add(username);
                        }
                    }
                    else
                    {
                        logstring += "\r\n获取服务器用户信息失败";
                    }
                    return usernameList;

                }
                catch (System.Exception ex)
                {
                    logstring = ex.Message;
                    return usernameList;
                }
                finally
                {
                    sqlcon.Close();
                }
            }
        }

        /// <summary>
        /// Get local services detail information
        /// </summary>
        /// <param name="serName"></param>
        public static void GetSqlServerServiceName(ref  List<ServiceController> serName)
        {
            ServiceController[] serControll = System.ServiceProcess.ServiceController.GetServices();
            foreach (ServiceController controler in serControll)
            {
                if (controler.ServiceName == "MSSQLSERVER" || controler.ServiceName == "MSSQL$SQLEXPRESS"  || controler.ServiceName=="SQLBrowser")
                {
                    serName.Add(controler);
                }
            }
        }

        /// <summary>
        /// Get services state
        /// </summary>
        /// <param name="serinfor"></param>
        /// <returns>stoped  running</returns>
        public static string GetServiceState(ServiceControllerEx serinfor)
        {
            if (serinfor.Status==ServiceControllerStatus.Stopped)
            {
                return "已停止";
            }
            else  if (serinfor.Status==ServiceControllerStatus.Running)
            {
                return "正在运行";
            }
            else if (serinfor.Status == ServiceControllerStatus.Paused)
            {
                return "服务已暂停";
            }
            else if (serinfor.Status == ServiceControllerStatus.PausePending)
            {
                return "服务即将暂停";
            }
            else if (serinfor.Status == ServiceControllerStatus.StartPending)
            {
                return "服务正在启动";
            }
            else if (serinfor.Status == ServiceControllerStatus.StopPending)
            {
                return "服务正在暂停";
            }
            else
            {
                return "";
            }
        }
 
        /// <summary>
        /// 增加用户
        /// </summary>
        /// <param name="ConnectionString">连接字符窜</param>
        /// <param name="username">用户名</param>
        /// <param name="databasename">数据库名</param>
        /// <param name="pwd">密码</param>
        /// <param name="pwdCheck">true：密码强制</param>
        /// <param name="logstring">错误提示</param>
        /// <returns>true：成功，false：失败(可能未密码强制所致)</returns>
        public static bool AddDataBaseUser(string ConnectionString, string username, string databasename, string pwd, bool pwdCheck, ref string logstring)
        {
            using (SqlConnection sqlcon = new SqlConnection(ConnectionString))
            {
                try
                {
                    SqlCommand sqlcom = new SqlCommand();
                    sqlcom.Connection = sqlcon;
                    string sqlstr = "CREATE LOGIN " + username + " WITH PASSWORD =" + "'" + pwd + "'" + ",CHECK_POLICY =";
                    sqlstr += pwdCheck ? "ON" : "OFF";
                    sqlstr += ",DEFAULT_DATABASE = " + databasename + ";" +
                             "EXEC sp_addsrvrolemember" + "'" + username + "'" + "," + "'sysadmin'";
                    sqlcom.CommandText = sqlstr;
                    sqlcon.Open();
                    sqlcom.ExecuteNonQuery();
                    return true;

                }
                catch (System.Exception ex)
                {
                    logstring += "\r\n" + ex.Message;
                    return false;
                }
                finally
                {
                    sqlcon.Close();
                }

            }
        }

        /// <summary>
        /// 将脚本转化为命令行（假设脚本中所有的GO及注释都是单独一行，且注释以/*开头）
        /// </summary>
        /// <param name="scriptname"></param>
        /// <returns></returns>
        private static List<string> ScriptToCommandLine(string scriptname)
        {
            List<string> scriptline = new List<string>();

            FileStream filestream = new FileStream(scriptname, FileMode.Open, FileAccess.Read);

            StreamReader reader = new StreamReader(filestream, System.Text.Encoding.Default);

            string commandText = "";

            string line;

            while ((line = reader.ReadLine()) != null)
            {

                if (line.ToString().Trim() == "GO" || line.ToString().Trim() == "Go" || line.ToString().Trim() == "go" || line.ToString().Trim() == "gO")
                {
                    scriptline.Add(commandText);
                    commandText = "";
                    continue;
                }
                else
                {

                    if (!line.StartsWith(@"/*") && line != "")
                    {
                        commandText += line;
                    }
                    else
                    {
                        continue;
                    }
                }
            }
            return scriptline;
        }
    }
}
